/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.sqoop.manager.oracle;

import static org.apache.sqoop.mapreduce.JobBase.HADOOP_MAP_TASK_MAX_ATTEMTPS;

/**
 * Constants for OraOop.
 */
public final class OraOopConstants {
  private OraOopConstants() {
  }

  public static final String ORAOOP_PRODUCT_NAME =
      "Data Connector for Oracle and Hadoop";
  public static final String ORAOOP_JAR_FILENAME = "oraoop.jar";

  // Disables OraOop - falling back to the OracleManager inside Sqoop...
  public static final String ORAOOP_DISABLED = "oraoop.disabled";

  // Whether to log Oracle session statistics using Guy Harrison's jar file...
  public static final String ORAOOP_REPORT_SESSION_STATISTICS =
      "oraoop.report.session.statistics";

  // Disables dynamic JDBC URL generation for each mapper...
  public static final String ORAOOP_JDBC_URL_VERBATIM =
      "oraoop.jdbc.url.verbatim";

  // The name of the Oracle RAC service each mapper should connect to, via their
  // dynamically generated JDBC URL...
  public static final String ORAOOP_ORACLE_RAC_SERVICE_NAME =
      "oraoop.oracle.rac.service.name";

  // The log4j log-level for OraOop...
  public static final String ORAOOP_LOGGING_LEVEL = "oraoop.logging.level";

  // The file names for the configuration properties of OraOop...
  public static final String ORAOOP_SITE_TEMPLATE_FILENAME =
      "oraoop-site-template.xml";
  public static final String ORAOOP_SITE_FILENAME = "oraoop-site.xml";

  // A flag that indicates that the OraOop job has been cancelled.
  // E.g. An Oracle DBA killed our Oracle session.
  // public static final String ORAOOP_JOB_CANCELLED = "oraoop.job.cancelled";

  // The SYSDATE from the Oracle database when this OraOop job was started.
  // This is used to generate unique names for partitions and temporary tables
  // that we create during the job...
  public static final String ORAOOP_JOB_SYSDATE = "oraoop.job.sysdate";

  // The properties are used internally by OraOop to indicate the schema and
  // name of
  // the table being imported/exported...
  public static final String ORAOOP_TABLE_OWNER = "oraoop.table.owner";
  public static final String ORAOOP_TABLE_NAME = "oraoop.table.name";

  // Constants used to indicate the desired location of the WHERE clause within
  // the SQL generated by the record-reader.
  // E.g. A WHERE clause like "rownum <= 10" would want to be located so that
  // it had an impact on the total number of rows returned by the split;
  // as opposed to impacting the number of rows returned for each of the
  // unioned data-chunks within each split.
  public static final String ORAOOP_TABLE_IMPORT_WHERE_CLAUSE_LOCATION =
      "oraoop.table.import.where.clause.location";

  /**
   * Location to place the WHERE clause.
   */
  public enum OraOopTableImportWhereClauseLocation {
    SUBSPLIT, SPLIT
  }

  // The SQL statements to execute for each new Oracle session that is
  // created...
  public static final String ORAOOP_SESSION_INITIALIZATION_STATEMENTS =
      "oraoop.oracle.session.initialization.statements";

  // Reliably stores the number mappers requested for the sqoop map-reduce
  // job...
  public static final String ORAOOP_DESIRED_NUMBER_OF_MAPPERS =
      "oraoop.desired.num.mappers";

  // The minimum number of mappers required for OraOop to accept the import
  // job...
  public static final String ORAOOP_MIN_IMPORT_MAPPERS =
      "oraoop.min.import.mappers";
  public static final int MIN_NUM_IMPORT_MAPPERS_ACCEPTED_BY_ORAOOP = 2;

  // The minimum number of mappers required for OraOop to accept the export
  // job...
  public static final String ORAOOP_MIN_EXPORT_MAPPERS =
      "oraoop.min.export.mappers";
  public static final int MIN_NUM_EXPORT_MAPPERS_ACCEPTED_BY_ORAOOP = 2;

  // The query used to fetch oracle data chunks...
  public static final String ORAOOP_ORACLE_DATA_CHUNKS_QUERY =
      "oraoop.oracle.data.chunks.query";

  // The minimum number of active instances in an Oracle RAC required for OraOop
  // to use dynamically generated JDBC URLs...
  public static final String ORAOOP_MIN_RAC_ACTIVE_INSTANCES =
      "oraoop.min.rac.active.instances";
  public static final int MIN_NUM_RAC_ACTIVE_INSTANCES_FOR_DYNAMIC_JDBC_URLS =
      2;

  // The name of the Oracle JDBC class...
  public static final String ORACLE_JDBC_DRIVER_CLASS =
      "oracle.jdbc.OracleDriver";

  // How many rows to pre-fetch when executing Oracle queries...
  public static final String ORACLE_ROW_FETCH_SIZE = "oracle.row.fetch.size";
  public static final int ORACLE_ROW_FETCH_SIZE_DEFAULT = 5000;

  // OraOop does not require a "--split-by" column to be defined...
  public static final String TABLE_SPLIT_COLUMN_NOT_REQUIRED = "not-required";

  // The name of the data_chunk_id column the OraOop appends to each (import)
  // query...
  public static final String COLUMN_NAME_DATA_CHUNK_ID = "data_chunk_id";

  // The hint that will be used on the SELECT statement for import jobs
  public static final String IMPORT_QUERY_HINT = "oraoop.import.hint";

  // Pseudo-columns added to an partitioned export table (created by OraOop from
  // a template table)
  // to store the partition value and subpartition value. The partition value is
  // the sysdate when
  // the job was performed. The subpartition value is the mapper index...
  public static final String COLUMN_NAME_EXPORT_PARTITION =
      "ORAOOP_EXPORT_SYSDATE";
  public static final String COLUMN_NAME_EXPORT_SUBPARTITION =
      "ORAOOP_MAPPER_ID";
  public static final String COLUMN_NAME_EXPORT_MAPPER_ROW =
      "ORAOOP_MAPPER_ROW";

  public static final String ORAOOP_EXPORT_PARTITION_DATE_VALUE =
      "oraoop.export.partition.date.value";
  public static final String ORAOOP_EXPORT_PARTITION_DATE_FORMAT =
      "yyyy-mm-dd hh24:mi:ss";

  // The string we want to pass to dbms_application_info.set_module() via the
  // "module_name" parameter...
  public static final String ORACLE_SESSION_MODULE_NAME = ORAOOP_PRODUCT_NAME;

  // The name of the configuration property containing the string we want to
  // pass to
  // dbms_application_info.set_module() via the "action_name" parameter...
  public static final String ORACLE_SESSION_ACTION_NAME =
      "oraoop.oracle.session.module.action";

  // Boolean whether to do a consistent read based off an SCN
  public static final String ORAOOP_IMPORT_CONSISTENT_READ =
      "oraoop.import.consistent.read";

  // The SCN number to use for the consistent read - calculated automatically -
  // cannot be overridden
  public static final String ORAOOP_IMPORT_CONSISTENT_READ_SCN =
      "oraoop.import.consistent.read.scn";

  // The method that will be used to create data chunks - ROWID ranges or
  // partitions
  public static final String ORAOOP_ORACLE_DATA_CHUNK_METHOD =
      "oraoop.chunk.method";

  /**
   * How should data be split up - by ROWID range, or by partition.
   */
  public enum OraOopOracleDataChunkMethod {
    ROWID, PARTITION
  }

  // List of partitions to be imported, comma seperated list
  public static final String ORAOOP_IMPORT_PARTITION_LIST =
      "oraoop.import.partitions";

  public static final OraOopOracleDataChunkMethod
                          ORAOOP_ORACLE_DATA_CHUNK_METHOD_DEFAULT =
                              OraOopOracleDataChunkMethod.ROWID;

  // How to allocate data-chunks into splits...
  public static final String ORAOOP_ORACLE_BLOCK_TO_SPLIT_ALLOCATION_METHOD =
      "oraoop.block.allocation";

  /**
   * How splits should be allocated to the mappers.
   */
  public enum OraOopOracleBlockToSplitAllocationMethod {
    ROUNDROBIN, SEQUENTIAL, RANDOM
  }

  // Whether to omit LOB and LONG columns during an import...
  public static final String ORAOOP_IMPORT_OMIT_LOBS_AND_LONG =
      "oraoop.import.omit.lobs.and.long";

  // Identifies an existing Oracle table used to create a new table as the
  // destination of a Sqoop export.
  // Hence, use of this property implies that the "-table" does not exist in
  // Oracle and OraOop should create it.
  public static final String ORAOOP_EXPORT_CREATE_TABLE_TEMPLATE =
      "oraoop.template.table";

  // If the table already exists that we want to create, should we drop it?...
  public static final String ORAOOP_EXPORT_CREATE_TABLE_DROP =
      "oraoop.drop.table";

  // If ORAOOP_EXPORT_CREATE_TABLE_TEMPLATE has been specified, then this flag
  // indicates whether the created Oracle
  // tables should have NOLOGGING...
  public static final String ORAOOP_EXPORT_CREATE_TABLE_NO_LOGGING =
      "oraoop.no.logging";

  // If ORAOOP_EXPORT_CREATE_TABLE_TEMPLATE has been specified, then this flag
  // indicates whether the created Oracle
  // tables should be partitioned by job and mapper...
  public static final String ORAOOP_EXPORT_CREATE_TABLE_PARTITIONED =
      "oraoop.partitioned";

  // Indicates (internally) the the export table we're dealling with has been
  // paritioned by OraOop...
  public static final String EXPORT_TABLE_HAS_ORAOOP_PARTITIONS =
      "oraoop.export.table.has.oraoop.partitions";

  // When using the Oracle hint... /* +APPEND_VALUES */ ...a commit must be
  // performed after each batch insert.
  // Therefore, the batches need to be quite large to avoid a performance
  // penality (for the 'extra' commits).
  // This is the minimum batch size to use under these conditions...
  public static final String ORAOOP_MIN_APPEND_VALUES_BATCH_SIZE =
      "oraoop.min.append.values.batch.size";
  public static final int ORAOOP_MIN_APPEND_VALUES_BATCH_SIZE_DEFAULT = 5000;

  // The version of the Oracle database we're connected to...
  public static final String ORAOOP_ORACLE_DATABASE_VERSION_MAJOR =
      "oraoop.oracle.database.version.major";
  public static final String ORAOOP_ORACLE_DATABASE_VERSION_MINOR =
      "oraoop.oracle.database.version.minor";

  // When OraOop creates a table for a Sqoop export (from a template table) and
  // the table contains partitions,
  // this is the prefix of those partition names. (This also allows us to later
  // identify partitions that OraOop
  // created.)
  public static final String EXPORT_TABLE_PARTITION_NAME_PREFIX = "ORAOOP_";

  // When OraOop creates temporary tables for each mapper during a Sqoop export
  // this is the prefix of table names...
  public static final String EXPORT_MAPPER_TABLE_NAME_PREFIX = "ORAOOP_";

  // The format string used to turn a DATE into a string for use within the
  // names of Oracle objects
  // that we create. For example, temporary tables, table partitions, table
  // subpartitions...
  public static final String ORACLE_OBJECT_NAME_DATE_TO_STRING_FORMAT_STRING =
      "yyyymmdd_hh24miss";

  // Indicates whether to perform a "merge" operation when performing a Sqoop
  // export.
  // If false, 'insert' statements will be used (i.e. no 'updates')...
  public static final String ORAOOP_EXPORT_MERGE = "oraoop.export.merge";

  // This property allows the user to enable parallelization during exports...
  public static final String ORAOOP_EXPORT_PARALLEL =
      "oraoop.export.oracle.parallelization.enabled";

  // Flag used to indicate that the Oracle table contains at least one column of
  // type BINARY_DOUBLE...
  public static final String TABLE_CONTAINS_BINARY_DOUBLE_COLUMN =
      "oraoop.table.contains.binary.double.column";
  // Flag used to indicate that the Oracle table contains at least one column of
  // type BINARY_FLOAT...
  public static final String TABLE_CONTAINS_BINARY_FLOAT_COLUMN =
      "oraoop.table.contains.binary.float.column";

  // The storage clause to append to the end of any CREATE TABLE statements we
  // execute for temporary Oracle tables...
  public static final String ORAOOP_TEMPORARY_TABLE_STORAGE_CLAUSE =
      "oraoop.temporary.table.storage.clause";

  // The storage clause to append to the end of any CREATE TABLE statements we
  // execute for permanent (export) Oracle tables...
  public static final String ORAOOP_EXPORT_TABLE_STORAGE_CLAUSE =
      "oraoop.table.storage.clause";

  // Additional columns to include with the --update-key column...
  public static final String ORAOOP_UPDATE_KEY_EXTRA_COLUMNS =
      "oraoop.update.key.extra.columns";

  // Should OraOop map Timestamps as java.sql.Timestamp as Sqoop does, or as
  // String
  public static final String ORAOOP_MAP_TIMESTAMP_AS_STRING =
      "oraoop.timestamp.string";
  public static final boolean ORAOOP_MAP_TIMESTAMP_AS_STRING_DEFAULT = true;

  // This flag allows the user to force use of the APPEND_VALUES Oracle hint
  // either ON, OFF or AUTO...
  public static final String ORAOOP_ORACLE_APPEND_VALUES_HINT_USAGE =
      "oraoop.oracle.append.values.hint.usage";

  /**
   * Whether to use the append values hint for exports.
   */
  public enum AppendValuesHintUsage {
    AUTO, ON, OFF
  }

  // http://download.oracle.com/docs/cd/E11882_01/server.112/e17118/
  //     sql_elements001.htm#i45441
  public static final String SUPPORTED_IMPORT_ORACLE_DATA_TYPES_CLAUSE =
      "(DATA_TYPE IN ("
          +
          // "'BFILE',"+
          "'BINARY_DOUBLE',"
          + "'BINARY_FLOAT',"
          + "'BLOB',"
          + "'CHAR',"
          + "'CLOB',"
          + "'DATE',"
          + "'FLOAT',"
          + "'LONG',"
          +
          // "'LONG RAW',"+
          // "'MLSLABEL',"+
          "'NCHAR',"
          + "'NCLOB',"
          + "'NUMBER',"
          + "'NVARCHAR2',"
          + "'RAW',"
          + "'ROWID',"
          +
          // "'UNDEFINED',"+
          "'URITYPE',"
          +
          // "'UROWID',"+ //<- SqlType = 1111 = "OTHER" Not supported as
          // "AAAAACAADAAAAAEAAF" is being returned as "AAAAAAgADAAAA"
          "'VARCHAR2'"
          + // <- Columns declared as VARCHAR are listed as VARCHAR2 in
            // dba_tabl_columns
          // "'XMLTYPE',"+
          ")" + " OR DATA_TYPE LIKE 'INTERVAL YEAR(%) TO MONTH'"
          + " OR DATA_TYPE LIKE 'INTERVAL DAY(%) TO SECOND(%)'"
          + " OR DATA_TYPE LIKE 'TIMESTAMP(%)'"
          + " OR DATA_TYPE LIKE 'TIMESTAMP(%) WITH TIME ZONE'"
          + " OR DATA_TYPE LIKE 'TIMESTAMP(%) WITH LOCAL TIME ZONE'" + ")";

  public static final String SUPPORTED_EXPORT_ORACLE_DATA_TYPES_CLAUSE =
      "(DATA_TYPE IN ("
          +
          // "'BFILE',"+
          "'BINARY_DOUBLE',"
          + "'BINARY_FLOAT',"
          +
          // "'BLOB',"+ //<- Jira: SQOOP-117 Sqoop cannot export LOB data
          "'CHAR',"
          +
          // "'CLOB',"+ //<- Jira: SQOOP-117 Sqoop cannot export LOB data
          "'DATE',"
          + "'FLOAT',"
          +
          // "'LONG',"+ //<- "create table as select..." and
          // "insert into table as select..." do not work when a long column
          // exists.
          // "'LONG RAW',"+
          // "'MLSLABEL',"+
          "'NCHAR',"
          +
          // "'NCLOB',"+ //<- Jira: SQOOP-117 Sqoop cannot export LOB data
          "'NUMBER',"
          + "'NVARCHAR2',"
          +
          // "'RAW',"+
          "'ROWID',"
          +
          // "'UNDEFINED',"+
          "'URITYPE',"
          +
          // "'UROWID',"+ //<- SqlType = 1111 = "OTHER" Not supported as
          // "AAAAACAADAAAAAEAAF" is being returned as "AAAAAAgADAAAA"
          "'VARCHAR2'"
          + // <- Columns declared as VARCHAR are listed as VARCHAR2 in
            // dba_tabl_columns
          // "'XMLTYPE',"+
          ")" + " OR DATA_TYPE LIKE 'INTERVAL YEAR(%) TO MONTH'"
          + " OR DATA_TYPE LIKE 'INTERVAL DAY(%) TO SECOND(%)'"
          + " OR DATA_TYPE LIKE 'TIMESTAMP(%)'"
          + " OR DATA_TYPE LIKE 'TIMESTAMP(%) WITH TIME ZONE'"
          + " OR DATA_TYPE LIKE 'TIMESTAMP(%) WITH LOCAL TIME ZONE'" + ")";

  // Query to get current logged on user
  public static final String QUERY_GET_SESSION_USER = "SELECT USER FROM DUAL";

  // public static final int[] SUPPORTED_ORACLE_DATA_TYPES = {
  // oracle.jdbc.OracleTypes.BIT // -7;
  // ,oracle.jdbc.OracleTypes.TINYINT // -6;
  // ,oracle.jdbc.OracleTypes.SMALLINT // 5;
  // ,oracle.jdbc.OracleTypes.INTEGER // 4;
  // ,oracle.jdbc.OracleTypes.BIGINT // -5;
  // ,oracle.jdbc.OracleTypes.FLOAT // 6;
  // ,oracle.jdbc.OracleTypes.REAL // 7;
  // ,oracle.jdbc.OracleTypes.DOUBLE // 8;
  // ,oracle.jdbc.OracleTypes.NUMERIC // 2;
  // ,oracle.jdbc.OracleTypes.DECIMAL // 3;
  // ,oracle.jdbc.OracleTypes.CHAR // 1;
  // ,oracle.jdbc.OracleTypes.VARCHAR // 12;
  // ,oracle.jdbc.OracleTypes.LONGVARCHAR // -1;
  // ,oracle.jdbc.OracleTypes.DATE // 91;
  // ,oracle.jdbc.OracleTypes.TIME // 92;
  // ,oracle.jdbc.OracleTypes.TIMESTAMP // 93;
  // // ,oracle.jdbc.OracleTypes.TIMESTAMPNS // -100; //<- Deprecated
  // ,oracle.jdbc.OracleTypes.TIMESTAMPTZ // -101;
  // ,oracle.jdbc.OracleTypes.TIMESTAMPLTZ // -102;
  // ,oracle.jdbc.OracleTypes.INTERVALYM // -103;
  // ,oracle.jdbc.OracleTypes.INTERVALDS // -104;
  // ,oracle.jdbc.OracleTypes.BINARY // -2;
  // /// ,oracle.jdbc.OracleTypes.VARBINARY // -3;
  // ,oracle.jdbc.OracleTypes.LONGVARBINARY // -4;
  // ,oracle.jdbc.OracleTypes.ROWID // -8;
  // ,oracle.jdbc.OracleTypes.CURSOR // -10;
  // ,oracle.jdbc.OracleTypes.BLOB // 2004;
  // ,oracle.jdbc.OracleTypes.CLOB // 2005;
  // // ,oracle.jdbc.OracleTypes.BFILE // -13;
  // // ,oracle.jdbc.OracleTypes.STRUCT // 2002;
  // // ,oracle.jdbc.OracleTypes.ARRAY // 2003;
  // ,oracle.jdbc.OracleTypes.REF // 2006;
  // ,oracle.jdbc.OracleTypes.NCHAR // -15;
  // ,oracle.jdbc.OracleTypes.NCLOB // 2011;
  // ,oracle.jdbc.OracleTypes.NVARCHAR // -9;
  // ,oracle.jdbc.OracleTypes.LONGNVARCHAR // -16;
  // // ,oracle.jdbc.OracleTypes.SQLXML // 2009;
  // // ,oracle.jdbc.OracleTypes.OPAQUE // 2007;
  // // ,oracle.jdbc.OracleTypes.JAVA_STRUCT // 2008;
  // // ,oracle.jdbc.OracleTypes.JAVA_OBJECT // 2000;
  // // ,oracle.jdbc.OracleTypes.PLSQL_INDEX_TABLE // -14;
  // ,oracle.jdbc.OracleTypes.BINARY_FLOAT // 100;
  // ,oracle.jdbc.OracleTypes.BINARY_DOUBLE // 101;
  // ,oracle.jdbc.OracleTypes.NULL // 0;
  // ,oracle.jdbc.OracleTypes.NUMBER // 2;
  // // ,oracle.jdbc.OracleTypes.RAW // -2;
  // // ,oracle.jdbc.OracleTypes.OTHER // 1111;
  // ,oracle.jdbc.OracleTypes.FIXED_CHAR // 999;
  // // ,oracle.jdbc.OracleTypes.DATALINK // 70;
  // ,oracle.jdbc.OracleTypes.BOOLEAN // 16;
  // };

  /**
   * Constants for things belonging to sqoop...
   */
  public static final class Sqoop {
    private Sqoop() {
    }

    /**
     * What type of Sqoop tool is being run.
     */
    public enum Tool {
      UNKNOWN, IMPORT, EXPORT
    }

    public static final String IMPORT_TOOL_NAME = "import";
  }

/**
 * Constants for things belonging to Oracle...
 */
  public static final class Oracle {
    private Oracle() {
    }

    public static final int ROWID_EXTENDED_ROWID_TYPE = 1;
    public static final int ROWID_MAX_ROW_NUMBER_PER_BLOCK = 32767;

    // This is how you comment-out a line of SQL text in Oracle.
    public static final String ORACLE_SQL_STATEMENT_COMMENT_TOKEN = "--";

    public static final String OBJECT_TYPE_TABLE = "TABLE";

    public static final String URITYPE = "URITYPE";

    public static final int MAX_IDENTIFIER_LENGTH = 30; // <- Max length of an
                                                        // Oracle name
                                                        // (table-name,
                                                        // partition-name etc.)

    public static final String HINT_SYNTAX = "/*+ %s */ "; // Syntax for a hint
                                                           // in Oracle
  }

  /**
   * Logging constants.
   */
  public static class Logging {
    /**
     * Level of log to output.
     */
    public enum Level {
      TRACE, DEBUG, INFO, WARN, ERROR, FATAL
    }
  }

}
